package tau_sadna.tau_map;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Scanner;

import android.annotation.SuppressLint;
import android.content.SharedPreferences.Editor;
import android.os.AsyncTask;
import android.util.Log;
import android.widget.Toast;

@SuppressLint("NewApi")
public class DownloadManager extends AsyncTask<MainActivity, String, Boolean> {
	static public final String Config = "Config.conf";
	static public final String Temp = ".tmp";
	static public final String Lay = ".lay";
	static public final String UpdateLog = "UpdateLog.log";
	
	static public final int FileNotFound = 0;
	static public final int IOError = -1;
	static public final int OK = 1;
	
	protected static boolean downloading = false;
	
	protected MainActivity mainAct;
	protected boolean wasUpdated;
	
	HashMap<String, Integer> VersionMap;  // version map for file on phone
	HashMap<String, Integer> UVersionMap; // updated version map.
	private File fdir;
	

	public static boolean isDownloading(){
		return downloading;
	}
	
	@Override
	protected void onPreExecute() {
		downloading = true;
		super.onPreExecute();
	}
	
	@Override
	protected Boolean doInBackground(MainActivity... act) {
		mainAct = act[0];
		wasUpdated = false;
		fdir = mainAct.getFilesDir();
		
		if(!updateConfigHash()){
			// not succeed.
			return Boolean.FALSE;
		}
		
		if(!download()){
			// // not succeed.
			return Boolean.FALSE;
		}
		
		if(!finishFileUpdate()){
			// not succeed.
			return Boolean.FALSE;
		}
		
		return Boolean.TRUE;
	}
	
	@Override
	protected void onPostExecute(Boolean result) {
		super.onPostExecute(result);
		
		if(result.booleanValue()){
			//update time.
			Editor editor = mainAct.sharedPreferences.edit();
			editor.putLong(MainActivity.LAST_TIME_DOWNLOAD, (new Date()).getTime());
			editor.apply();
			
			if(wasUpdated){
				mainAct.requestRestart();
			} else {
				Toast.makeText(mainAct, "No update wes needed", Toast.LENGTH_LONG).show();
			}
		} else {
			Toast.makeText(mainAct, "Could not download files", Toast.LENGTH_LONG).show();
		}
		
		downloading = false;
	}
	
	@Override
	protected void onCancelled() {
		downloading = false;
		super.onCancelled();
	}
	
	@Override
	protected void onCancelled(Boolean result) {
		downloading = false;
		super.onCancelled(result);
	}
	
	/**
	 * finish the download, in case of error will just need to download again. 
	 * @return true if succeed, and false if failed. 
	 */
	protected boolean finishFileUpdate() {
		// for every key in UVersionMap check if was updated.
		Iterator<String> keys = UVersionMap.keySet().iterator();
		while(keys.hasNext()){
			String key = keys.next();
			File newFile = new File(fdir, key+Lay+Temp);
			if(newFile.exists()){
				// was updated.
				File old = new File(fdir, key+Lay);
				
				if(old.exists()){
					old.delete();
				}
				
				if(!newFile.renameTo(old)){
					//error.
					return false;
				}
			}
		}
		
		// for every key in UVersionMap check if was deleted.
		keys = VersionMap.keySet().iterator();
		while(keys.hasNext()){
			String key = keys.next();
			if(!UVersionMap.containsKey(key)){
				// was deleted.
				File old = new File(fdir, key+Lay);
				
				if(old.exists()){
					if(!old.delete()){
						//error.
						return false;
					}
				}
			}
		}
		
		// update config.
		File old = new File(fdir, Config);
		File newFile = new File(fdir, Config+Temp);
		
		boolean bool = old.exists();
		if(old.exists()){
			bool = old.delete();
		}
		
		bool = newFile.renameTo(old);
		if(!bool){
			// error
			return false;
		} 
		
		// delete updateLog.
		old = new File(fdir, UpdateLog);
		if(old.exists()){
			old.delete();
		}
		
		return true;
	}

	/**
	 * download files.
	 * @return true if succeed, and false if failed.
	 */
	protected boolean download() {
		// for every key in UVersionMap check if file need to be updated, and update.
		Iterator<String> keys = UVersionMap.keySet().iterator();
		while(keys.hasNext()){
			String key = keys.next();
			if(!VersionMap.containsKey(key) || VersionMap.get(key).intValue() != UVersionMap.get(key).intValue()){
				wasUpdated = true;
				
				//downloading.
				if(!Communication.downloadItem(key, key+Lay+Temp)){
					return false;
				}
				
				FileIO.addToConfLog(UpdateLog, key, UVersionMap.get(key).intValue());
			}
		}
		
		return true;
	}

	/**
	 * @param fdir - the directory where the files should be. 
	 * @return true on Success and false on failure.
	 */
	protected boolean updateConfigHash() {
		VersionMap = new HashMap<String, Integer>();
		UVersionMap = new HashMap<String, Integer>();
				
		if(!Communication.downloadConfig(Config+Temp)){
			// could not download.
			return false;
		}
		
		// get VersionMap values from file.
		if(updateVersionMap(VersionMap, Config) == IOError){
			//error
			return false;
		}
		
		int tmp = updateVersionMap(VersionMap, UpdateLog);
		if(tmp == IOError){
			//error
			return false;
		}
		if(tmp == OK){
			// UpdateLog exist so need to update (not always from server).
			wasUpdated = true;
		}
		
		if(updateVersionMap(UVersionMap, Config+Temp) != OK){
			//error, file should be exist.
			return false;
		}
		
		return true;
	}

	/**
	 * 
	 * @param map - the map to be updated.
	 * @param fdir - the directory where the files should be.
	 * @param fname - file name.
	 * @return OK on Success, IOError on IOException, and File not found if file not found.
	 */
	protected int updateVersionMap(HashMap<String, Integer> map, String fname) {
		String line;
		BufferedReader in = null;
		
		try {
			in = new BufferedReader(new FileReader(new File(fdir, fname)));
			while ((line = in.readLine()) != null) {
				// read a single line and update map.
				Scanner sc = new Scanner(line);
				String name;
				Integer version;
				
				while(sc.hasNext()){
					String[] stArr = sc.next().split(":");
					if(stArr.length != 2)
						continue;
					name = stArr[0];
					version = Integer.valueOf(stArr[1]);
					if(version == null){
						continue;
					}
					
					map.put(name, version);
				}
				
				sc.close();			
			}
			in.close();
		} catch (FileNotFoundException e) {
			Log.e("IO_read", "file not found: "+e.toString());
			return FileNotFound;
		} catch (IOException e) {
			Log.e("IO_read", "io exception: "+e.toString());
			return IOError;
		}
		
		return OK;
	}

}
